package edu.luc.cs413s13.team1.monsterm.model.cell;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Semaphore;

import android.util.Log;
import edu.luc.cs413s13.team1.monsterm.model.common.CellChangeListener;
import edu.luc.cs413s13.team1.monsterm.model.common.Constants;
import edu.luc.cs413s13.team1.monsterm.model.monster.Actor;

public class MonstermCell implements Cell {

	/**
	 * Constructs a cell with the given capacity.
	 */
	public MonstermCell(int capacity, CellChangeListener listener) {
	    sema = new Semaphore(capacity);
	    this.cellChangeListener = listener;
	}
	protected CellChangeListener cellChangeListener;

	
	private static Random random = new Random(System.currentTimeMillis());

	  /**
	   * The neighboring cells of this cell.
	   */
	  protected ArrayList<Cell> neighbors = new ArrayList<Cell>();

	  /**
	   * The current occupants of this cell.
	   */
	  protected ArrayList<Actor> occupants = new ArrayList<Actor>();

	  /**
	   * A semaphore to control entry into this limited-capacity cell.
	   */
	  protected Semaphore sema;


	  /**
	   * This method adds a neighbor to this cell in a thread-safe manner.
	   */
	 public synchronized void addNeighbor(Cell cell) { neighbors.add(cell); }

	  /**
	   * This method adds an occupant to this cell in a thread-safe manner.
	   */
	  protected synchronized void addOccupant(Actor actor) { occupants.add(actor); }

	  /**
	   * This method removes an occupant from this cell in a thread-safe manner.
	   */
	  protected synchronized void removeOccupant(Actor actor) { occupants.remove(actor); }

	  /**
	   * This method waits for space to open in this cell, if necessary, and then
	   * places the actor inside the cell.
	   */
	  public void enter(Actor actor) throws InterruptedException {
	    Cell previous = actor.getCell();
		Log.i(Constants.TAG, "enter: Entering cell "+ this);
		if (this != previous) {
	      // if necessary, wait for space in this cell
	      sema.acquire();
	      // if the actor was somewhere else before, take them out of there
	      if (previous != null) {
	        previous.leave(actor);
	      }
	      // put the actor into this cell
	      actor.setCell(this);
	      addOccupant(actor);
	      // fire event to tell all occupants of this cell about the new arrival
	      onEnterCell(new CellEvent(this, actor));
	    } else {
	    	Log.i(Constants.TAG, "Staying in same cell");
	    }
	  }

	  public void leave(Actor actor) {
		Log.i(Constants.TAG, "leave: Leaving cell "+ this);
	    removeOccupant(actor);
	    // fire event to tell all occupants of this cell about the departure
	    onLeaveCell(new CellEvent(this, actor));
	    // release the space that was occupied by the actor who just left
	    sema.release();
	  }

	  /**
	   * This method fires an CellEvent.enterCell event
	   * to all occupants of this cell.
	   */
	  @SuppressWarnings("unchecked")
	  public void onEnterCell(final CellEvent event) {
		  // Notify listener
		  this.cellChangeListener.onEnterCell(event);
	    List<Actor> currentOccupants;
	    synchronized (this) {
	      currentOccupants = (List<Actor>) this.occupants.clone();
	    }
	    for (Actor a : currentOccupants) {
	      a.onEnterCell(event);
	    }
	  }

	  /**
	   * This method fires an <code>CellEvent.leaveCell</code> event
	   * to all occupants of this cell.
	   */
	  @SuppressWarnings("unchecked")
	  public void onLeaveCell(final CellEvent event) {
		  // Notify listener
		  this.cellChangeListener.onLeaveCell(event);
	    List<Actor> currentOccupants;
	    synchronized (this) {
	      currentOccupants = (List<Actor>) this.occupants.clone();
	    }
	    for (Actor a : currentOccupants) {
	      a.onEnterCell(event);
	    }
	  }

	  /**
	   * This method returns a clone of this cell's neighbors.
	   */
	  @SuppressWarnings("unchecked")
	  public synchronized List<Cell> getNeighbors() { return (List<Cell>) neighbors.clone(); }

	  /**
	   * This method returns a clone of this cell's occupants.
	   */
	  @SuppressWarnings("unchecked")
	  public synchronized List<Actor> getOccupants() { return (List<Actor>) occupants.clone(); }

	  public synchronized Cell randomNeighbor() {
	    int size = neighbors.size();
	    return size == 0 ? null : (Cell) neighbors.get(random.nextInt(size));
	  }

}
